package start.spring.basic.util.common;

import java.math.BigDecimal;

/**
 * ****************************************************************************
 * 数字运算工具类，用于解决浮点数运算丢失精度的问题
 *
 * @author(作者)：xuyongyun	
 * @author(复审人)：	Administrator 2018-3-16
 * @version(版本)1.0
 * @source(来源)http://blog.csdn.net/hknock/article/details/51133463
 * @since 1.0
 ******************************************************************************
 */

public class MathUtil {

	/**
	 * 默认运算精度，2位小数
	 * 根据需求传入值
	 */
	private static final int DEFAULT_SCALE = 2;
	
	/**
	 * 默认运算精度，2位小数
	 * 根据需求传入值
	 */
	private static final int ROUND_MAX = 8;
	
	/**
	 * 默认运算精度，2位小数
	 * 根据需求传入值
	 */
	private static final int ROUND_MIN = 0;
	
	
	/**
	 * 默认舍入模式
	 * 根据需求传入值
	 * ROUND_CEILING 向正无穷方向舍入
	 * ROUND_DOWN    向零方向舍入
	 * ROUND_FLOOR   向负无穷方向舍入
	 * ROUND_HALF_DOWN 向（距离）最近的一边舍入，除非两边（的距离）是相等
	 *                 如果是这样，向下舍入，例如1.55 保留一位小数结果为1.5
	 * ROUND_HALF_EVEN 向（距离）最近的一边舍入，除非两边（的距离）是相等,
	 *                 如果保留位数是奇数，使用ROUND_HALF_UP，
	 *                 如果保留位数是偶数，使用ROUND_HALF_DOWN
	 * ROUND_HALF_UP   向（距离）最近的一边舍入，除非两边（的距离）是相等,
	 *                 如果是这样，向上舍入，1.55保留一位小数结果为1.6
	 * ROUND_UNNECESSARY 计算结果是精确的，不需要舍入模式
	 * 
	 * ROUND_UP 向远离0的方向舍入
	 * 
	 */
	private static final int DEFAULT_ROUND_MODE = BigDecimal.ROUND_HALF_UP;
	
	/**
	  * 提供精确的加法运算
	  * 为了精确计算，用String来够造BigDecimal
	  * @param v1  
	  * @param v2
	  * @return 两个参数加和，以字符串格式返回
	  */
	public static String add(String v1, String v2){
		BigDecimal b1 = new BigDecimal(v1);
		BigDecimal b2 = new BigDecimal(v2);
		return b1.add(b2).toPlainString();
	}

	/**
	  * 提供精确的减法运算
	  * 为了精确计算，用String来够造BigDecimal
	  * @param v1
	  * @param v2
	  * @return 两个参数差v1-v2，以字符串格式返回
	  */
	public static String sub(String v1, String v2){
		BigDecimal b1 = new BigDecimal(v1);
	    BigDecimal b2 = new BigDecimal(v2);
	    return b1.subtract(b2).toPlainString();
	}
	
	/**
	  * 提供精确的乘法运算
	  * @param v1
	  * @param v2
	  * @return 两个参数的数学积，以字符串格式返回
	  */

	public static String multi(String v1, String v2){
		BigDecimal b1 = new BigDecimal(v1);
	    BigDecimal b2 = new BigDecimal(v2);
	    return b1.multiply(b2).toPlainString();
	}
	
	/**
	  * 提供（相对）精确的除法运算。当发生除不尽的情况时，则”精度“、”舍入模式“采用程序默认值
	  * @param v1
	  * @param v2
	  * @return 两个参数的商v1/v2，以字符串格式返回
	  */
	public static String divide(String v1, String v2){
		BigDecimal b1 = new BigDecimal(v1);
	    BigDecimal b2 = new BigDecimal(v2);
	    return b1.divide(b2, DEFAULT_SCALE, DEFAULT_ROUND_MODE).toPlainString();
	}
	
	/**
	  * 提供（相对）精确的除法运算。当发生除不尽的情况时，则”舍入模式“采用程序默认值
	  * @param v1
	  * @param v2
	  * @param scale 表示需要精确到小数点以后几位，传入不合法值时采用程序默认值
	  * @return 两个参数的商v1/v2，以字符串格式返回
	  */
	public static String divide(String v1, String v2, int scale){
		//传入不合法值时采用程序默认值
		if(scale<0){
			scale = DEFAULT_SCALE;
		}
		
		BigDecimal b1 = new BigDecimal(v1);
	    BigDecimal b2 = new BigDecimal(v2);
	    return b1.divide(b2, scale, DEFAULT_ROUND_MODE).toPlainString();
	}

	/**
	  * 提供（相对）精确的除法运算。当发生除不尽的情况时，则指定”精度“、”舍入模式“
	  * @param v1
	  * @param v2
	  * @param scale 表示需要精确到小数点以后几位，传入不合法值时采用程序默认值
	  * @param round_mode 表示用户指定的舍入模式，传入不合法值时采用程序默认值
	  * @return 两个参数的商v1/v2，以字符串格式返回
	  */
	public static String divide(String v1, String v2, int scale, int roundMode){
		//传入不合法值时采用程序默认值
		if(scale<0){
			scale = DEFAULT_SCALE;
		}
		
		//传入不合法值时采用程序默认值
		boolean roundModeOk = roundMode<MathUtil.ROUND_MIN||roundMode>MathUtil.ROUND_MAX;
		if(roundModeOk){
			roundMode = DEFAULT_ROUND_MODE;
		}
		
		BigDecimal b1 = new BigDecimal(v1);
	    BigDecimal b2 = new BigDecimal(v2);
	    return b1.divide(b2, scale, roundMode).toPlainString();
	}

	/**
	  * 提供精确的小数位四舍五入处理，采用程序默认的舍入模式
	  * 
	  * @param v 需要四舍五入的数字 ，传入不合法值时采用程序默认值
	  * @param scale 小数点后保留几位
	  * 
	  * @return 四舍五入后的结果，以字符串格式返回
	  */
	public static String round(String v, int scale){
		
		//传入不合法值时采用程序默认值
		if(scale<0){
			scale = DEFAULT_SCALE;
		}
		
		return round(v, scale, DEFAULT_ROUND_MODE);
	}

	/**
	  * 提供精确的小数位四舍五入处理
	  * 
	  * @param v 需要四舍五入的数字
	  * 
	  * @param scale 小数点后保留几位，传入不合法值时采用程序默认值
	  * @param round_mode 指定的舍入模式，传入不合法值时采用程序默认值
	  * 
	  * @return 四舍五入后的结果，以字符串格式返回
	  */
	public static String round(String v, int scale, int roundMode){
		
		//传入不合法值时采用程序默认值
		if(scale<0){
			scale = DEFAULT_SCALE;
		}
		
		//传入不合法值时采用程序默认值
		boolean roundModeOk = roundMode<MathUtil.ROUND_MIN||roundMode>MathUtil.ROUND_MAX;
		if(roundModeOk){
			roundMode = DEFAULT_ROUND_MODE;
		}

	   BigDecimal b = new BigDecimal(v);
	   return b.setScale(scale, roundMode).toPlainString();
	}
}
